package cn.heycm.blog.console.domain.admin.service;

import cn.heycm.blog.commom.cache.listener.RemoveListener;
import cn.heycm.blog.commom.cache.service.CacheService;
import cn.heycm.blog.commom.cache.wrapper.ValueWrapper;
import cn.heycm.blog.common.entity.constant.CacheSet;
import cn.heycm.blog.common.entity.constant.Constant;
import cn.heycm.blog.common.entity.constant.SmsTemplate;
import cn.heycm.blog.common.entity.result.CodeMsg;
import cn.heycm.blog.common.entity.session.Session;
import cn.heycm.blog.common.sms.entity.SmsRequest;
import cn.heycm.blog.common.sms.entity.SmsResponse;
import cn.heycm.blog.common.sms.service.SmsService;
import cn.heycm.blog.common.utils.Assert;
import cn.heycm.blog.common.utils.Jackson;
import cn.heycm.blog.common.utils.RandomUtil;
import cn.heycm.blog.console.domain.admin.repository.AdminRepository;
import cn.heycm.blog.console.infra.dao.admin.entity.AdminProfile;
import cn.heycm.blog.console.domain.admin.model.Administrator;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author hey
 * @version 1.0
 * @since 2024/9/15 19:08
 */
@Service
@AllArgsConstructor
@Slf4j
public class AdminServiceImpl implements AdminService {

    private final Environment environment;

    private final SmsService smsService;
    private final CacheService cacheService;
    private final RemoveListener defaultRemoveListener;

    private final AdminRepository adminRepository;

    @Override
    public Administrator findAdministrator(String phoneId) {
        AdminProfile profile = adminRepository.findByPhoneId(phoneId);
        if (null == profile) {
            return null;
        }
        return Administrator.builder()
                .id(profile.getAdminId())
                .roleId(profile.getRoleId())
                .phoneId(profile.getPhoneId())
                .enable(profile.getEnable())
                .username(profile.getUsername())
                .nickname(profile.getNickname())
                .build();
    }

    @Override
    public void sendVerifyCode(Administrator administrator) {
        log.info("Admin发送登录验证码: {}", Jackson.toJson(administrator));
        String code = "123456";
        int ttl = 5;
        String env = environment.getProperty(Constant.ENV_MODE, Constant.PROD_MODE);
        if (Constant.PROD_MODE.equals(env)) {
            Assert.isTrue(administrator.ifAllowLogin(), CodeMsg.DENY_LOGIN);
            code = RandomUtil.randomNumber(6);
            SmsResponse response = smsService.send(SmsRequest.builder()
                    .to(new String[]{administrator.getPhoneId()})
                    .templateId(SmsTemplate.VERIFY_CODE)
                    .templateData(Map.of("code", code, "ttl", ttl + ""))
                    .signature(SmsTemplate.SIGNATURE)
                    .build());
            Assert.isTrue(response.isOk(), CodeMsg.SEND_VERIFY_CODE_FAIL);
        }
        String key = CacheSet.format(CacheSet.LOGIN_VERIFY_CODE, administrator.getPhoneId());
        ValueWrapper valueWrapper = ValueWrapper.of(code, ttl, TimeUnit.MINUTES);
        valueWrapper.addRemoveListener(defaultRemoveListener);
        cacheService.set(key, valueWrapper);
    }

    @Override
    public void checkVerifyCode(Administrator administrator, String verifyCode) {
        String key = CacheSet.format(CacheSet.LOGIN_VERIFY_CODE, administrator.getPhoneId());
        String code = cacheService.get(key, String.class);
        Assert.isTrue(StringUtils.hasText(code) && code.equals(verifyCode), CodeMsg.VERIFY_CODE_INVALID);
        cacheService.del(key);
    }

    @Override
    public Session login(Administrator administrator, String verifyCode) {
        this.checkVerifyCode(administrator, verifyCode);
        Session session = administrator.buildSession();
        String key = CacheSet.format(CacheSet.LOGIN_SESSION, administrator.getPhoneId());
        cacheService.set(key, session, 1, TimeUnit.DAYS);
        return session;
    }

}
